from django.shortcuts import render

# Create your views here.
import  pickle
import base64

from django_redis import get_redis_connection
from rest_framework.response import Response
from rest_framework.views import APIView

from cart.serializers import CartSerializer,CartSKUSerializer,CartSKUIDSerializer
from goods.models import SKU

"""
1、我们没有登陆也可以把商品放入购物车     购物车的数据是放在客户端
                                        放在cookie中
    登陆肯定可以将商品放入购物车          购物车的数据是保存在服务端
                                        mysql
                                        redis
2、没有登陆要保存的信息 ：商品id，商品数量，选中状态
    登陆要保存的信息：用户的id，商品id，商品数量，选中状态
3、没有登陆要保存的信息：组织数据结构
        cart = {
            sku_id :{count:5,selected:True},
            sku_id :{count:4,selected:False},
        }
    登陆用户要保存的信息：保存到redis中
        redis的数据是保存在内存中的，原则是：尽量少的占用内存空间

    Hash 哈希 ：       key：   field：value
                               field2：value2
                               field3：value3
    Set  集合 ：       key：  [value2,value1,value3]





"""


# cart = {
#     '1':{'count':5,'selected':True}
# }
#
# # 将字符串转换为bytes类型
# dumps = pickle.dumps(cart)
# #将bytes类型转换为字典
# pickle.loads(dumps)
#
# import base64
# # 将bytes类型进行编码
# base64.b64encode(dumps)
#
# base64.b64decode('gAN9cQBYAQAAADFxAX1xAihYCAAAAHNlbGVjdGVkcQOIWAUAAABjb3VudHEESwV1cy4=')
#
#
# import pickle
# import base64
# #1、编码处理
# cart = {
#     '1':{'count':5,'selected':True}
# }
# #2、将字典转换为bytes类型
# dumps = pickle.dumps(cart)
# #3、将bytes类型的数据重新进行base64编码
# encode = base64.b64encode(dumps)
# #4、将编码之后的bytes类型的数据转换为字符串
# encode.decode()
# #5、解码数据
# #将我们的数据转换为bytes类型
# decode = base64.b64decode(encode)
# #6、将bytes类型转换为字典
# pickle.loads(decode)

import redis
from redis import StrictRedis,Redis

from rest_framework import status

class CartAPIView(APIView):
    """
    我们的APIView在调用我们的http方法前会进行认证
    业务逻辑：token过期或者伪造的话 认为是一个匿名用户
        匿名用户的购物车数据可以放在cookie中
        对于我们而言，用该能够先进入到http方法中，等需要验证用户信息的时候，再验证


    """

    def perform_authentication(self, request):

        pass
    """
    当用户点击添加购物车的时候，
    登陆用户需要将token_id,sku_id,count,选中的状态提交给后端
    未登录用户需要将 sku_id,count，选中的状态提交给后端

    1、后端接受数据（sku_id,count,selected）
    2、验证数据(验证商品id时候有对应的商品，商品的个数)
    3、获取数据
    4、得到用户的信息
    5、根据用户的信息进行判断
    6、登陆用户保存在redis中
        6.1 连接redis
        6.2 保存数据 hash set
        6.3 返回响应
    7、未登录用户保存在cookie中
        7.1先读取cookie信息
        7.2判断是否有购物车信息
            如果有则是加密的数据
            如果没有
        7.3判断这个商品是否在cookie中
            如果在 则累加数据
            如果没有 则直接添加
        7.4将购物车数据进行加密处理
        7.5返回响应


    """
    #  添加到购物车

    def post(self,request):
        # 1、后端接受数据（sku_id, count, selected）
        data = request.data
        # 2、验证数据(验证商品id时候有对应的商品，商品的个数)
        serializer = CartSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        # 3、获取数据
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')
        # 4、得到用户的信息
        # 因为我们是在需要判断的时候再去判断 用户信息，所以需要对用户信息进行异常的捕获
        try:
            user = request.user
        except Exception as e:
            user = None
        # 5、根据用户的信息进行判断
        #is_authenticated  认证用户
        if user is not None and user.is_authenticated:
            # 6、登陆用户保存在redis中
            # 6.1、链接redis
            redis_conn = get_redis_connection('cart')
            # 6.2保存数据
            # hash
            # redis_conn.hset('cart_%s'%user.id,sku_id,count)
            #hincrby 累加操作 累加的数据可以是正数也可以是负数

            """
            管道
            管道是基础redis类的子类，它为在大哥请求中向服务器缓冲多个命令提供支持
            他们可用于通过减少客户端和服务器之间来回tcp数据包的数量来显示着提高命令组的性能

            """

            #①创建管道
            pl = redis_conn.pipeline()
            #②将redis指令添加到管道中
            pl.hincrby('cart_%s'%user.id,sku_id,count)

            # set
            if selected:
                pl.sadd('cart_selected_%s'%user.id,sku_id)

            #③执行管道
            pl.execute()

            # 6.3 返回响应
            return Response(serializer.data)

        else:
            # 7、未登录用户保存在cookie中
            # 7.1 先读取cookie信息
            cookie_str = request.COOKIES.get('cart')
            # 7.2 判断是否有购物车信息
            if cookie_str is not None:
                # 如果有则是加密的数据
                # 将加密之后的数据进行base64解码
                decode = base64.b64decode(cookie_str)
                # 需要将二进制转换为字典
                cookie_cart = pickle.loads(decode)
            else:
                # 如果没有,则定义一个空字典
                cookie_cart = {}
            # 7.3 判断这个商品是否在cookie中
            if sku_id in cookie_cart:
                # 如果在 则累加数据
                original_count = cookie_cart[sku_id]['count']
                count += original_count
            # 如果没有 则直接添加
            cookie_cart[sku_id]={
                'count':count,
                'selected':selected
            }
            # 7.4 将购物车数据进行加密处理
            dumps = pickle.dumps(cookie_cart)
            encode = base64.b64encode(dumps)
            # 7.5 返回响应
            response = Response(serializer.data)

            response.set_cookie('cart',encode.decode())

            return response


    """
    1、获取用户信息
    2、根据用户信息进行判断
    3、登陆用户从redis中获取数据
        3.1、链接reids
        3.2、获取hash，set
        3.3、都获取 商品id，count，selected
    4、未登录用户从cookie中获取数据
        4.1、获取cookie数据
        4.2、判断cart数据
            如果有则进行解密
            如果没有则为空
        4.3、都获取 商品id，count,selected
    5、根据id去获取商品的详细信息sku
    6、将对象转换为字典

    """
    #查询购物车数据

    def get(self,request):
        # 1、获取用户信息
        try:
            user = request.user
        except Exception as e:
            user = None
        # 2、根据用户信息进行判断
        if user is not None and user.is_authenticated:
            # 3、登陆用户从redis中获取数据
            #       3.1、链接reids
            redis_conn = get_redis_connection('cart')
            #     3.2、获取hash，
            sku_id_counts = redis_conn.hgetall('cart_%s'%user.id)
            #   set
            selected_ids = redis_conn.smembers('cart_selected_%s'%user.id)
            #     3.3、都获取 商品id，count，selected

            """
            因为redis获取的数据格式 和 cookie获取的数据格式不一样
            所以根据id去获取商品的详细信息的业务逻辑不能统一

            需要将redis数据转换为cookie格式
            redis数据获取之后都是bytes

            cookie_cart = {sku_id:{count:xxx,selected:True}}
            """
            cookie_cart = {}
            for sku_id,count in sku_id_counts.items():
                # if sku_id in selected_ids:
                #     flag = True
                # else:
                #     flag = False
                    # 因为redis获取的数据都是bytes类型
                    # 需要将它转换为int类型
                cookie_cart[int(sku_id)] = {
                    'count':int(count),
                    'selected':sku_id in selected_ids   #  flag
                }
        else:
            # 4、未登录用户从cookie中获取数据
            #     4.1、获取cookie数据
            cookie_str = request.COOKIES.get('cart')
            #     4.2、判断cart数据
            if cookie_str is not None:
                #         如果有则进行解密
                cookie_cart = pickle.loads(base64.b64decode(cookie_str))
            else:
                #         如果没有则为空
                cookie_cart = {}
                #     4.3、都获取 商品id，count,selected
        # 5、根据id去获取商品的详细信息sku
        ids = cookie_cart.keys()
        skus = SKU.objects.filter(pk__in=ids)
        #遍历
        for sku in skus:
            sku.count = cookie_cart[sku.id]['count']
            sku.selected = cookie_cart[sku.id]['selected']
        # 6、将对象转换为字典
        serializer = CartSKUSerializer(skus,many=True)

        return Response(serializer.data)

    """
    前端需要将token(可选),sku_id,count,selected 提交给后端

    1、接收数据
    2、验证数据
    3、获取数据
    4、获取用户信息
    5、根据用户的状态进行判断
    6、登陆用户更新redis
        6.1、连接redis
        6.2、更新hash，set
        6.3、返回数据
    7、未登录用户更新cookie
        7.1、先获取cart数据
        7.2、判断cart数据
            如果有则解密
            如果没有则初始化
        7.3、更新数据
        7.4、对数据进行加密
        7.5、返回数据

    """
    # 修改购物车数据

    def put(self,request):
        # 1、接收数据
        data = request.data
        # 2、验证数据
        serializer = CartSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        # 3、获取数据
        sku_id =serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')
        # 4、获取用户信息
        try:
            user = request.user
        except Exception as e:
            user = None
        # 5、根据用户的状态进行判断
        if user is not None and user.is_authenticated:
            # 6、登陆用户更新redis
            #     6.1、连接redis
            redis_conn = get_redis_connection('cart')
            #     6.2、更新hash，
            redis_conn.hset('cart_%s'%user.id,sku_id,count)
            #   set
            if selected:
                redis_conn.sadd('cart_%s'%user.id,sku_id)
            else:
                redis_conn.srem('cart_%s'%user.id,sku_id)
            #     6.3、返回数据
            return Response(serializer.data)
        else:
            # 7、未登录用户更新cookie
            #     7.1、先获取cart数据
            cookie_str = request.COOKIES.get('cart')
            #     7.2、判断cart数据
            if cookie_str is not None:
                #         如果有则解密
                cookie_cart = pickle.loads(base64.b64decode(cookie_str))
            else:
                #         如果没有则初始化
                cookie_cart = {}
            #     7.3、更新数据
            if sku_id in cookie_cart:
                cookie_cart[sku_id] = {
                    'count':count,
                    'selected':selected
                }
            #     7.4、对数据进行加密
            cookie_encode = base64.b64encode(pickle.dumps(cookie_cart))
            #     7.5、返回数据
            response = Response(serializer.data)

            response.set_cookie('cart',cookie_encode.decode())

            return response


    """
    当用户点击某一个商品的删除按钮的时候，需要让前端就爱那个sku_id 和 token 传递geihouduan

    1、接收数据
    2、验证数据
    3、获取数据
    4、获取用户信息
    5、根据用户信息进行判断
    6、登陆用户需要更新redis
        6.1连接redis
        6.2hash，set
        6.3返回响应
    7、未登录用户需要更新cookie
        7.1获取cart数据
        7.2判断cart数据
        7.3删除数据
        7.4对字典进行加密处理
        7.5返回响应
    """

    # 删除购物车数据

    def delete(self,request):
        # 1、接收数据
        data = request.data
        # 2、验证数据
        serializer =CartSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        # 3、获取数据
        sku_id = serializer.validated_data['sku_id']
        # 4、获取用户信息
        try:
            user = request.user
        except Exception as e:
            user = None
        # 5、根据用户信息进行判断
        if user is not None and user.is_authenticated:
            # 6、登陆用户需要更新redis
            #     6.1连接redis
            redis_conn = get_redis_connection('cart')
            #     6.2hash，
            redis_conn.hdel('cart_%s'%user.id,sku_id)
            # set
            redis_conn.srem('cart_selected_%s'%user.id,sku_id)
            #     6.3返回响应
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            # 7、未登录用户需要更新cookie
            #     7.1获取cart数据
            cookie_str = request.COOKIES.get('cart')
            #     7.2判断cart数据
            if cookie_str is not None:
                cookie_cart = pickle.loads(base64.b64decode(cookie_str))
            else:
                cookie_cart = {}
            #     7.3删除数据
            if sku_id in cookie_cart:
                del cookie_cart[sku_id]
            #     7.4对字典进行加密处理
            cookie_encode = base64.b64encode(pickle.dumps(cookie_cart))
            #     7.5返回响应
            response = Response(status=status.HTTP_204_NO_CONTENT)

            response.set_cookie('cart',cookie_encode.decode())

            return response

































